﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Lexical.Coding;
using Kokomo.Lexical.Expressions;
using System.Diagnostics;

namespace Kokomo.PeInspector.Reflection.Disassembly.Flow
{
	class ConditionFlowAnalyzer : FlowAnalyzer
	{
		public ConditionFlowAnalyzer(
			FlowAnalyzer parent,
			Expression condition,
			int blockStartOffset,
			int blockEndOffset,
			int bodyStartOffset,
			int bodyEndOffset
			)
			: base(parent, blockStartOffset, blockEndOffset, bodyStartOffset, bodyEndOffset)
		{
			if (condition == null) throw new ArgumentNullException("condition");
			this.ConditionStatement = new ConditionStatement(condition);
			this.Condition = condition;

			this.ActiveStatementBlock = this.ConditionStatement.TrueStatements;
		}

		public int ElseStartOffset { get; private set; }

		private Expression _condition;
		public Expression Condition
		{
			get { return this._condition; }
			set
			{
				this._condition = value;
				this.ConditionStatement.Condition = value;
			}
		}

		private ConditionStatement _conditionStatement;
		public ConditionStatement ConditionStatement
		{
			get { return this._conditionStatement; }
			private set
			{
				this._conditionStatement = value;
				if (value != null)
					value.Condition = this.Condition;
			}
		}
		public Expression TrueExpression { get; private set; }
		public Expression FalseExpression { get; private set; }

		public SwitchStatement SwitchStatement { get; internal set; }

		protected override void OnExitBlock()
		{
			base.OnExitBlock();

			if (this.TrueExpression != null)
			{
				MsilStackEntry falseEntry = this.Context.PopStackEntry();
				this.FalseExpression = falseEntry.Expression;

				ConditionalExpression conditionalExpression = new ConditionalExpression(this.Condition, this.TrueExpression, this.FalseExpression);
				this.Context.PushStackEntry(conditionalExpression, falseEntry.ExpressionType);

				this.ConditionStatement = null;
			}
		}

		//protected override void AnalyzeConditionalBranch(MsilBranch branch)
		//{
		//    MsilDecompilerContext context = this.Context;

		//    if (
		//        (this.ActiveStatementBlock == this.ConditionStatement.TrueStatements)
		//        && (this.ActiveStatementBlock.Count == 0)
		//        && (context.ExpressionCount == 1)
		//        )
		//    {
		//        Expression newCondition = context.PopExpression();

		//        // Short circuit logic
		//        if (branch.TargetOffset == this.BlockEndOffset)
		//        {
		//            this.Condition = Expression.op_LogicalAnd(this.Condition, newCondition);
		//        }
		//        else
		//        {
		//        }
		//    }
		//    else
		//    {
		//        base.AnalyzeConditionalBranch(branch);
		//    }
		//}


		protected override void AnalyzeInnerBranch(MsilBranch branch, FlowAnalyzer innerFlow)
		{
			if (
				(this.Context.CurrentInstruction.NextOffset == this.BodyEndOffset)
				&& (branch.TargetOffset > this.BlockEndOffset)
				&& (
					this.ParentAnalyzer.BodyContains(branch.TargetOffset)
					|| (branch.TargetOffset == this.ParentAnalyzer.BodyEndOffset)
					)
				)
			{
				// This indicates the presence of an else block
				this.ElseStartOffset = this.BodyEndOffset;
				this.ExtendBlockTo(branch.TargetOffset);
				this.BodyEndOffset = branch.TargetOffset;

				if (this.ElseStartOffset < this.BodyEndOffset)
				{
					this.ActiveStatementBlock = this.ConditionStatement.FalseStatements;
				}
			}

			base.AnalyzeInnerBranch(branch, innerFlow);
		}

		protected override void AnalyzeLocalConditionalBranch(MsilBranch branch)
		{
			if (
				this.IsBranchToSubsequentBlock(branch)
				&& (this.ActiveStatementBlock == this.ConditionStatement.TrueStatements)
				&& (this.ActiveStatementBlock.Count == 0)
				)
			{
				Expression condition = this.Context.PopExpression();
				this.Condition = Expression.op_LogicalAnd(this.Condition, !condition);
				this.Context.Advance();
			}
			else if (
				(this.Context.CurrentInstruction.NextOffset == this.BlockEndOffset)
				&& (this.ParentAnalyzer.BodyContains(branch.TargetOffset))
				&& (this.ConditionStatement.TrueStatements.Count == 0)
				&& (this.TrueExpression == null)
				)
			{
				Expression newConditionClause = this.Context.PopExpression();
				newConditionClause = newConditionClause.CoerceTo(typeof(bool));
				newConditionClause = !newConditionClause;
				newConditionClause = newConditionClause.CoerceTo(typeof(bool));

				Expression combinedCondition = Expression.op_LogicalOr(!this.Condition, newConditionClause);
				this.Condition = combinedCondition;
				this.ExtendBlockTo(branch.TargetOffset);
				this.BodyEndOffset = branch.TargetOffset;
				this.BodyStartOffset = this.Context.CurrentInstruction.NextOffset;

				this.Context.Advance();
			}
			else
			{
				base.AnalyzeLocalConditionalBranch(branch);
			}
		}

		protected override void AnalyzeLocalBranch(MsilBranch branch)
		{
			if (
				(this.Context.CurrentInstruction.NextOffset == this.BodyEndOffset)
				&& (branch.TargetOffset > this.BlockEndOffset)
				&& (
					this.ParentAnalyzer.BlockContains(branch.TargetOffset)
				// Allows nested "if" blocks
					|| (branch.TargetOffset == this.ParentAnalyzer.BodyEndOffset)
					)
				)
			{
				// This indicates the presence else block
				this.ElseStartOffset = this.BodyEndOffset;
				this.ExtendBlockTo(branch.TargetOffset);
				this.BodyEndOffset = branch.TargetOffset;

				if (
					(this.ConditionStatement.TrueStatements.Count == 0)
					&& (this.Context.ExpressionCount > 0)
					)
				{
					this.TrueExpression = this.Context.PopExpression();
				}

				if (this.ElseStartOffset < this.BodyEndOffset)
				{
					this.ActiveStatementBlock = this.ConditionStatement.FalseStatements;
				}

				this.Context.Advance();
			}
			else
			{
				base.AnalyzeLocalBranch(branch);
			}
		}
	}
}
